﻿using System;
using System.CodeDom.Compiler;
using System.ComponentModel.Composition;
using Accountant.Properties;
using System.Linq;

namespace Accountant.DataModel
{
  [Export, PartCreationPolicy(CreationPolicy.Shared)]
  partial class AccountantContext
  {
    public static void CleanUnboundContext()
    {
      Unbound = new UnboundAccountantContext();
    }
    public static AccountantContext Unbound = new UnboundAccountantContext();
    class UnboundAccountantContext : AccountantContext
    {
      public override string ToString()
      {
        return "Unbound";
      }
    }

    protected override Database GetDatabase()
    {
      var database = new Database(Settings.Default.Database);
      database.SetInitializer(new DbInitializer());
      return database;
    }


    protected override void LoadCore(Database db)
    {
      int index;

      foreach (var chunk in db.GetChunks("Currencies"))
        using (var r = db.OpenChunkRead(chunk))
        {
          var length = r.BaseStream.Length;
          while (r.BaseStream.Position < length)
          {
            var currency = new Currency();
            currency.Id = Currencies.Count;
            currency.Name = r.ReadString();
            currency.Symbol = r.ReadString();
            currency.Comment = r.ReadString();
            currency.SymbolPosition = (SymbolPosition)r.ReadInt32();
            Currencies.Add(currency);
          }
        }


      foreach (var chunk in db.GetChunks("Accounts"))
        using (var r = db.OpenChunkRead(chunk))
        {
          var length = r.BaseStream.Length;
          while (r.BaseStream.Position < length)
          {
            var account = new Account();
            account.Id = Accounts.Count;
            account.Name = r.ReadString();
            account.Comment = r.ReadString();
            account.LastRevisionDate = r.ReadDateTime();
            account.IsAbstract = r.ReadBoolean();
            account.IsSealed = r.ReadBoolean();
            account.IsHidden = r.ReadBoolean();
            account.IsPrimaryCurrencyExclusive = r.ReadBoolean();
            index = r.ReadInt32();
            if (index != -1) account.BaseAccount = Accounts[index];
            index = r.ReadInt32();
            if (index != -1) account.PrimaryCurrency = Currencies[index];
            Accounts.Add(account);
          }
        }

      foreach (var chunk in db.GetChunks("Transactions"))
        using (var r = db.OpenChunkRead(chunk))
        {
          var length = r.BaseStream.Length;
          while (r.BaseStream.Position < length)
          {
            var transaction = new Transaction();
            transaction.Id = Transactions.Count;
            transaction.Timestamp = r.ReadDateTime();
            transaction.Comment = r.ReadString();
            transaction.Amount = r.ReadDecimal();
            index = r.ReadInt32();
            if (index != -1) transaction.Debit = Accounts[index];
            index = r.ReadInt32();
            if (index != -1) transaction.Credit = Accounts[index];
            index = r.ReadInt32();
            if (index != -1) transaction.Currency = Currencies[index];
            Transactions.Add(transaction);
          }
        }

      foreach (var chunk in db.GetChunks("ExchangeRates"))
        using (var r = db.OpenChunkRead(chunk))
        {
          var length = r.BaseStream.Length;
          while (r.BaseStream.Position < length)
          {
            var item = new ExchangeRate();
            item.Id = ExchangeRates.Count;
            item.StartAt = r.ReadDateTime();
            index = r.ReadInt32();
            if (index != -1) item.CurrencyFrom = Currencies[index];
            index = r.ReadInt32();
            if (index != -1) item.CurrencyTo = Currencies[index];
            item.Rate = r.ReadDecimal();
            item.Scale = r.ReadDecimal();
            item.Source = (ExchangeRateSources) r.ReadInt16();
            ExchangeRates.Add(item);
          }
        }
    }
    protected override void SaveCore(Database db)
    {
      foreach (var chunk in db.BrakeToChunks("Currencies", Currencies))
        using (var w = db.OpenChunkWrite(chunk.FileName))
          for (var i = chunk.From; i < chunk.To; i++)
          {
            var item = Currencies[i];
            w.WriteString(item.Name);
            w.WriteString(item.Symbol);
            w.WriteString(item.Comment);
            w.Write((int)item.SymbolPosition);
            item.Id = i;
          }
      var sortedAccounts = Accounts
        .OrderBy(item => item.BaseAccount == null ? -1 : item.BaseAccount.Id)
        .ToList();

      foreach (var chunk in db.BrakeToChunks("Accounts", Accounts))
        using (var w = db.OpenChunkWrite(chunk.FileName))
          for (var i = chunk.From; i < chunk.To; i++)
          {
            var item = sortedAccounts[i];
            w.WriteString(item.Name);
            w.WriteString(item.Comment);
            w.Write(item.LastRevisionDate); 
            w.Write(item.IsAbstract);
            w.Write(item.IsSealed);
            w.Write(item.IsHidden);
            w.Write(item.IsPrimaryCurrencyExclusive);
            w.Write(item.BaseAccount == null ? -1 : item.BaseAccount.Id);
            w.Write(item.PrimaryCurrency == null ? -1 : item.PrimaryCurrency.Id);
            item.Id = i;
          }

      foreach (var chunk in db.BrakeToChunks("Transactions", Transactions))
        using (var w = db.OpenChunkWrite(chunk.FileName))
          for (var i = chunk.From; i < chunk.To; i++)
          {
            var item = Transactions[i];
            w.Write(item.Timestamp);
            w.WriteString(item.Comment);
            w.Write(item.Amount);
            w.Write(item.Debit == null ? -1 : item.Debit.Id);
            w.Write(item.Credit == null ? -1 : item.Credit.Id);
            w.Write(item.Currency == null ? -1 : item.Currency.Id);
            item.Id = i;
          }

      foreach (var chunk in db.BrakeToChunks("ExchangeRates", ExchangeRates))
        using (var w = db.OpenChunkWrite(chunk.FileName))
          for (var i = chunk.From; i < chunk.To; i++)
          {
            var item = ExchangeRates[i];
            w.Write(item.StartAt);
            w.Write(item.CurrencyFrom == null ? -1 : item.CurrencyFrom.Id);
            w.Write(item.CurrencyTo == null ? -1 : item.CurrencyTo.Id);
            w.Write(item.Rate);
            w.Write(item.Scale);
            w.Write((Int16)item.Source);
            item.Id = i;
          }
    }
    protected override void Dump(IndentedTextWriter writer)
    {
      writer.WriteLine("Currencies ({0})", Currencies.Count);
      writer.WriteLine("{");
      writer.Indent++;
      foreach (var currency in Currencies)
      {
        writer.WriteLine("Currency");
        writer.WriteLine("{");
        writer.Indent++;
        currency.Dump(writer);
        writer.Indent--;
        writer.WriteLine("}");
      }
      writer.Indent--;
      writer.WriteLine("}");

      writer.WriteLine("Accounts ({0})", Accounts.Count);
      writer.WriteLine("{");
      writer.Indent++;
      foreach (var account in Accounts)
      {
        writer.WriteLine("Account");
        writer.WriteLine("{");
        writer.Indent++;
        account.Dump(writer);
        writer.Indent--;
        writer.WriteLine("}");
      } 
      writer.Indent--;
      writer.WriteLine("}");

      writer.WriteLine("Transactions ({0})", Transactions.Count);
      writer.WriteLine("{");
      writer.Indent++;
      foreach (var transaction in Transactions)
      {
        writer.WriteLine("Transaction");
        writer.WriteLine("{");
        writer.Indent++;
        transaction.Dump(writer);
        writer.Indent--;
        writer.WriteLine("}");
      } 
      writer.Indent--;
      writer.WriteLine("}");

      writer.WriteLine("ExchangeRates ({0})", ExchangeRates.Count);
      writer.WriteLine("{");
      writer.Indent++;
      foreach (var item in ExchangeRates)
      {
        writer.WriteLine("ExchangeRates");
        writer.WriteLine("{");
        writer.Indent++;
        item.Dump(writer);
        writer.Indent--;
        writer.WriteLine("}");
      } 
      writer.Indent--;
      writer.WriteLine("}");
    }
  }
}